#pragma once

#define DEFAULT_NUM_TOKENS			5000
#define MAX_TOKENS					64
#define MAX_LINES					50000
#define MAX_HEX_BYTES_PER_LINE		32
#define MAX_STRING_LENGTH			256
#define MAX_SCRIPT_NAME				8
#define MAX_COMMAND_NAME			80
#define MAX_IDENTIFIER_NAME			80
#define MAX_TYPE_NAME				32
#define MAX_OPCODES					0x8000U
#define MAX_COMMANDS				0x8000
#define MAX_OPERATORS				24
#define MAX_OPERATIONS				62
#define MAX_OPERATOR_CHARS			3
#define MAX_PARAMS					32
#define MAX_TRANSLATION_ELEMENTS	8
#define MAX_TRANSLATION_CONDITIONS	8
#define OPCODE_NOT_MASK				0x8000U

typedef short tOpcode;

enum eTypeIndex
{
	// Must match CSyntax::s_pTypeNames
	TYPE_NONE,
	TYPE_INT,
	TYPE_FLOAT,
	TYPE_CONST,
	TYPE_TEXT_LABEL,
	TYPE_STRING,
	TYPE_VAR,
	TYPE_VAR_INT,
	TYPE_VAR_FLOAT,
	TYPE_VAR_TEXT_LABEL,
	TYPE_VAR_STRING,
	TYPE_LVAR,
	TYPE_LVAR_INT,
	TYPE_LVAR_FLOAT,
	TYPE_LVAR_TEXT_LABEL,
	TYPE_LVAR_STRING,
	TYPE_VAR_ARRAY,
	TYPE_VAR_INT_ARRAY,
	TYPE_VAR_FLOAT_ARRAY,
	TYPE_VAR_TEXT_LABEL_ARRAY,
	TYPE_VAR_STRING_ARRAY,
	TYPE_LVAR_ARRAY,
	TYPE_LVAR_INT_ARRAY,
	TYPE_LVAR_FLOAT_ARRAY,
	TYPE_LVAR_TEXT_LABEL_ARRAY,
	TYPE_LVAR_STRING_ARRAY,

	// ...?
	TYPE_VAR_CONST,
	TYPE_LVAR_CONST,
	TYPE_VAR_CONST_ARRAY,
	TYPE_LVAR_CONST_ARRAY,

	MAX_TYPES
};
enum PType : BYTE
{
	PT_NONE,
	PT_INT						= 0x1,
	PT_FLOAT					= 0x2,
	PT_NUMBER					= PT_INT | PT_FLOAT,
	PT_TEXT_LABEL				= 0x4,
	PT_STRING					= 0x8,
	PT_ANYSTRING				= PT_TEXT_LABEL | PT_STRING,
	PT_CONST					= 0x10,
	PT_ANYBASIC					= PT_NUMBER | PT_ANYSTRING,		// number, string, text label
	PT_ANYVALUE					= PT_ANYBASIC | PT_CONST,
	PT_VAR						= 0x20,
	PT_VAR_INT					= PT_VAR | PT_INT,
	PT_VAR_FLOAT				= PT_VAR | PT_FLOAT,
	PT_VAR_CONST				= PT_VAR | PT_CONST,
	PT_VAR_TEXT_LABEL			= PT_VAR | PT_TEXT_LABEL,
	PT_VAR_STRING				= PT_VAR | PT_STRING,
	PT_LVAR						= 0x40,
	PT_LVAR_INT					= PT_LVAR | PT_INT,
	PT_LVAR_FLOAT				= PT_LVAR | PT_FLOAT,
	PT_LVAR_CONST				= PT_LVAR | PT_CONST,
	PT_LVAR_TEXT_LABEL			= PT_LVAR | PT_TEXT_LABEL,
	PT_LVAR_STRING				= PT_LVAR | PT_STRING,
	PT_ARRAY					= 0x80,
	PT_ANYVAR					= PT_VAR | PT_LVAR,
	PT_VAR_ARRAY				= PT_ARRAY | PT_VAR,
	PT_LVAR_ARRAY				= PT_ARRAY | PT_LVAR,
	PT_VAR_INT_ARRAY			= PT_VAR_ARRAY | PT_INT,
	PT_VAR_FLOAT_ARRAY			= PT_VAR_ARRAY | PT_FLOAT,
	PT_VAR_CONST_ARRAY			= PT_VAR_ARRAY | PT_CONST,
	PT_VAR_TEXT_LABEL_ARRAY		= PT_VAR_ARRAY | PT_TEXT_LABEL,
	PT_VAR_STRING_ARRAY			= PT_VAR_ARRAY | PT_STRING,
	PT_LVAR_INT_ARRAY			= PT_LVAR_ARRAY | PT_INT,
	PT_LVAR_FLOAT_ARRAY			= PT_LVAR_ARRAY | PT_FLOAT,
	PT_LVAR_CONST_ARRAY			= PT_LVAR_ARRAY | PT_CONST,
	PT_LVAR_TEXT_LABEL_ARRAY	= PT_LVAR_ARRAY | PT_TEXT_LABEL,
	PT_LVAR_STRING_ARRAY		= PT_LVAR_ARRAY | PT_STRING,
	PT_ANYARRAY					= PT_ARRAY,
};
enum eArgFlag : WORD
{
	ARGFLAG_NONE,
	ARGFLAG_RETURN				= 0x1,
	ARGFLAG_SIZE_LIMITED		= 0x2,
	ARGFLAG_NUM_EXTRA_PARAMS	= 0x4,
	ARGFLAG_VAR_EXTRA_PARAMS	= 0x8,
	ARGFLAG_PARSABLE			= 0x10,
	ARGFLAG_UNKNOWN_PARAM		= 0x20,
	ARGFLAG_OMISSION			= 0x40,		// (default parameter)
	ARGFLAG_OPERAND				= 0x80,
	ARGFLAG_ANY					= 0x100,
	ARGFLAG_LEFT_OPERAND		= ARGFLAG_OPERAND | ARGFLAG_RETURN,
	ARGFLAG_RIGHT_OPERAND		= ARGFLAG_OPERAND,
	ARGFLAG_AMBIGUOUS			= ARGFLAG_UNKNOWN_PARAM | ARGFLAG_ANY,
};
inline eArgFlag operator|(eArgFlag& l,const eArgFlag& r)			{return static_cast<eArgFlag>((int)l | (int)r);}
inline eArgFlag operator&(eArgFlag& l,const eArgFlag& r)			{return static_cast<eArgFlag>((int)l & (int)r);}
inline eArgFlag& operator|=(eArgFlag& l,const eArgFlag& r)			{return l = static_cast<eArgFlag>(l | r);}
inline eArgFlag& operator&=(eArgFlag& l,const eArgFlag& r)			{return l = static_cast<eArgFlag>(l & r);}
/*typedef struct _ArgFlag
{
	union
	{
		struct
		{
			BYTE		bReturn				: 1;
			BYTE		bSizeLimit			: 1;
			BYTE		bNumExtraParams		: 1;
			BYTE		bVarArg				: 1;
			BYTE		bParsable			: 1;
			BYTE		bUnknown			: 1;
			BYTE		bDefaultParam		: 1;
			BYTE		bAny				: 1;
		} m_Flags;
		eArgFlag m_eFlag;
	};
} ArgFlag;*/

enum eExtendedType : WORD
{
	ET_NONE,
	ET_FIRST,
	ET_LABEL = ET_FIRST,
	ET_MODEL,
	NUM_BUILT_IN_EXTENDED_TYPES,
	MAX_BUILT_IN_EXTENDED_TYPES = 64,			// reserve some space for future additions - extended types begin here
};
typedef struct _TypeIndex
{
	union
	{
		eTypeIndex m_eIdx;
		int m_nIdx;
	};
	_TypeIndex() : m_eIdx(TYPE_NONE)
	{}
	_TypeIndex(eTypeIndex idx) : m_eIdx(idx)
	{}
	_TypeIndex(int n) : m_nIdx(n)
	{}
} TypeIndex;
typedef struct _ExtendedTypeIndex
{
	union
	{
		eExtendedType m_eIdx;
		WORD m_wIdx;
	};
	_ExtendedTypeIndex() : m_eIdx(ET_NONE)
	{}
	_ExtendedTypeIndex(eExtendedType v) : m_eIdx(v)
	{}
	_ExtendedTypeIndex(WORD v) : m_wIdx(v)
	{}
} ExtendedTypeIndex;
/*enum eTypeToken
{
	TYPE_TOKEN_NONE,
	TYPE_TOKEN_LABEL,
	TYPE_TOKEN_MODEL,
	TYPE_TOKEN_STRING,
	TYPE_TOKEN_CONSTANT,
};*/
enum eDataType
{
	// data types for the compiler in translations (types.def)
	DT_NULL,
	DT_INT8								= 0x01,
	DT_INT16							= 0x02,
	DT_INT24							= 0x03,
	DT_INT32							= 0x04,
	DT_FLOAT8							= 0x05,
	DT_FLOAT16							= 0x06,
	DT_FLOAT24							= 0x07,
	DT_FLOAT32							= 0x08,
	DT_FIXED16							= 0x09,

	// + for the internal script engine
	DT_LABEL							= 0x0A,
	DT_TEXT_LABEL						= 0x0B,
	DT_STRING							= 0x0C,
	DT_VAR_INT							= 0x0D,
	DT_VAR_FLOAT						= 0x0E,
	DT_VAR_TEXT_LABEL					= 0x0F,
	DT_VAR_STRING						= 0x10,
	DT_LVAR_INT							= 0x11,
	DT_LVAR_FLOAT						= 0x12,
	DT_LVAR_TEXT_LABEL					= 0x13,
	DT_LVAR_STRING						= 0x14,
	DT_VAR_INT_ARRAY					= 0x15,
	DT_VAR_FLOAT_ARRAY					= 0x16,
	DT_VAR_TEXT_LABEL_ARRAY				= 0x17,
	DT_VAR_STRING_ARRAY					= 0x18,
	DT_LVAR_INT_ARRAY					= 0x19,
	DT_LVAR_FLOAT_ARRAY					= 0x1A,
	DT_LVAR_TEXT_LABEL_ARRAY			= 0x1B,
	DT_LVAR_STRING_ARRAY				= 0x1C,
	DT_TEXT_LABEL_16					= 0x1D
	// ...?
};
enum eTokenType
{
	TOKEN_NONE,

	// Script identifiers
	TOKEN_TYPENAME,
	TOKEN_VAR,
	TOKEN_VARINDEX,		// for unregistered vars
	TOKEN_LABEL,
	TOKEN_OPERATOR,
	TOKEN_COMMAND,
	TOKEN_VALUE,
	TOKEN_STRING,
	TOKEN_CONSTRUCT,
	TOKEN_CONSTANT,
	TOKEN_MODEL,
	TOKEN_ENUM,

	// HEX
	TOKEN_HEX,
	TOKEN_HEX_BYTE,
	TOKEN_HEX_WORD,
	TOKEN_HEX_DWORD,
	TOKEN_HEXMAYBELABEL,
	TOKEN_CHAR_ARRAY,		// raw string

	// Preprocessor
	TOKEN_DIRECTIVE,
	TOKEN_MACRO,
	TOKEN_MACROPARAM,
	TOKEN_HANDLED,
	TOKEN_BIN_FILE,

	// ASM
	TOKEN_ASM,
	TOKEN_ASM_DISPLACE,
	TOKEN_ASM_DISPLACE_END,
	TOKEN_ASM_LABEL_POINTER,

	// for the compiler to know how many args of one type to compile
	TOKEN_VARARG_NUM_ARGS,

	// script engine tokens
	TOKEN_SCRAMBL_CLASS,
	TOKEN_SCRAMBL_VAR,
	TOKEN_SCRAMBL_REGISTER,

	TOKEN_UNKNOWN = 255,
};

enum
{
	SCRIPT_NONE,
	SCRIPT_NAME,
	SCRIPT_IF,
	SCRIPT_ANDOR,
	SCRIPT_ELSE,
	SCRIPT_ENDIF,
	SCRIPT_GOTO,
	SCRIPT_ELSE_GOTO,
	SCRIPT_END,
	SCRIPT_START,
	SCRIPT_CASE,
	SCRIPT_WHEN,
	SCRIPT_ENDCASE,
	SCRIPT_REPEAT,
	SCRIPT_ENDREPEAT,
	SCRIPT_WHILE,
	SCRIPT_WHILENOT,
	SCRIPT_ENDWHILE,
	SCRIPT_MAX_PARSED_OPCODES,
};
enum eDefinitions
{
	DEF_MAX_CONDITIONS,
	DEF_MAX_VAR_INDEX,
	DEF_NUM_BYTES_IN_ARRAY_FLAG,
	DEF_MAIN_VAR_BLOCK_OFFSET,
	DEF_LOGICAL_AND,
	DEF_LOGICAL_OR,
	DEF_LOGICAL_NOT,
	DEF_PREFIX_LOGICAL_AND,
	DEF_PREFIX_LOGICAL_OR,
	DEF_PREFIX_LOGICAL_NOT,
};
enum
{
	SCR_TRUE = 1,
	SCR_FALSE,
	SCR_AND,
	SCR_OR,
	SCR_NOT,
	SCR_BREAK,
	SCR_CONTINUE,
	SCR_IF,
	SCR_ENDIF,
	SCR_WHILE,
	SCR_ENDWHILE,
	SCR_REPEAT,
	SCR_ENDREPEAT,
	SCR_ELSE,
	SCR_CASE,
	SCR_WHEN,
	SCR_ENDCASE,
	SCR_MISSION_START,
	NUM_LANG_CONSTRUCTS = 40,
};
enum eDirectives
{
	DIRECTIVE_NONE,
	DIRECTIVE_PUSH_CONFIG,
	DIRECTIVE_POP_CONFIG,
	DIRECTIVE_INCLUDE,
	DIRECTIVE_IMPORT,
	DIRECTIVE_CONST_BEGIN,
	DIRECTIVE_CONST_END,
	DIRECTIVE_HEX_BEGIN,
	DIRECTIVE_HEX_END,
	DIRECTIVE_ASM_BEGIN,
	DIRECTIVE_ASM_END,
	DIRECTIVE_DEFINE,
	DIRECTIVE_UNDEF,
	DIRECTIVE_IF,
	DIRECTIVE_ELSE,
	DIRECTIVE_ENDIF,
	DIRECTIVE_ERROR,
	DIRECTIVE_WARNING,
	DIRECTIVE_REGISTER_COMMAND,
	DIRECTIVE_REGISTER_OPERATOR,
	DIRECTIVE_LOAD_TYPES,
	DIRECTIVE_REGISTER_TYPE,
	DIRECTIVE_REGISTER_VAR,
	DIRECTIVE_HEADER,
	DIRECTIVE_ENDHEADER,
	DIRECTIVE_CONSOLE,
	DIRECTIVE_ENDCONSOLE,
	DIRECTIVE_FUNCTION,
	DIRECTIVE_ENDFUNCTION,
	DIRECTIVE_EACH,
	DIRECTIVE_ENDEACH,
	NUM_DIRECTIVES
};

enum ePunctuators
{
	//PUNC_PERIOD				= '.',
	PUNC_COMMA				= ',',
	PUNC_COLON				= ':',
	PUNC_LCURLYBRACKET		= '{',
	PUNC_RCURLYBRACKET		= '}',
	PUNC_LBRACKET			= '(',
	PUNC_RBRACKET			= ')',
	PUNC_LSQUAREBRACKET		= '[',
	PUNC_RSQUAREBRACKET		= ']',
	PUNC_LANGLEBRACKET		= '<',
	PUNC_RANGLEBRACKET		= '>',
	PUNC_HASH				= '#',
	PUNC_EQUAL				= '=',
	PUNC_PLUS				= '+',
	PUNC_MINUS				= '-',
	PUNC_ASTERISK			= '*',
	PUNC_SLASH				= '/',
	PUNC_BSLASH				= '\\',
	PUNC_AMPERSAND			= '&',
	PUNC_AT					= '@',
	PUNC_DOLLAR				= '$',
};
enum eSymbols
{
	SYMBOL_LABEL				= PUNC_COLON,
	SYMBOL_SCOPE_BEGIN			= PUNC_LCURLYBRACKET,
	SYMBOL_SCOPE_END			= PUNC_RCURLYBRACKET,
	SYMBOL_HIGHLIGHT_BEGIN		= PUNC_LBRACKET,
	SYMBOL_HIGHLIGHT_END		= PUNC_RBRACKET,
	SYMBOL_ARRAY_LBRACKET		= PUNC_LSQUAREBRACKET,
	SYMBOL_DIRECTIVE			= PUNC_HASH,
	SYMBOL_SCRAMBL_CLASS		= PUNC_AT,
	//SYMBOL_SCRAMBL_VAR			= PUNC_PERIOD,
	SYMBOL_SCRAMBL_REGISTER		= PUNC_DOLLAR
};
enum eOperators
{
	OPERATOR_ASSIGN			= PUNC_EQUAL,
	OPERATOR_ADD			= PUNC_PLUS,
	OPERATOR_SUBTRACT		= PUNC_MINUS,
	OPERATOR_MULTIPLY		= PUNC_ASTERISK,
	OPERATOR_DIVIDE			= PUNC_SLASH,
	OPERATOR_CONVERT		= PUNC_HASH,
	OPERATOR_DELTA			= PUNC_AT,
	OPERATOR_EQUAL			= PUNC_EQUAL,
	OPERATOR_GREATER		= PUNC_RANGLEBRACKET,
	OPERATOR_LESSER			= PUNC_LANGLEBRACKET,
};
enum eExprOp {
	EXPR_OP_INVALID,
	EXPR_OP_ASSIGN,
	EXPR_OP_ADD,
	EXPR_OP_SUBTRACT,
	EXPR_OP_MULTIPLY,
	EXPR_OP_DIVIDE,
	EXPR_OP_EQUAL,
	EXPR_OP_INEQUAL,
	EXPR_OP_GREATER,
	EXPR_OP_LESSER,
	EXPR_OP_EQGREATER,
	EXPR_OP_EQLESSER,
	EXPR_OP_INC,
	EXPR_OP_DEC,
	EXPR_OP_NEG,
	EXPR_OP_POS,
	EXPR_OP_BIT_AND,
	EXPR_OP_BIT_OR,
	EXPR_OP_BIT_XOR,
	EXPR_OP_BIT_NOT,
	EXPR_OP_BIT_LSH,
	EXPR_OP_BIT_RSH,
	EXPR_OP_BIT_MOD,
};

enum eTranslateElement
{
	TRANSLATE_ELEM_NONE,
	TRANSLATE_ELEM_VAL,
	TRANSLATE_ELEM_COND,
	TRANSLATE_ELEM_OPERAND,
};
enum eTranslateCondition
{
	TRANSLATE_COND_NONE,
	TRANSLATE_COND_SIZEOF_VAL,
	TRANSLATE_COND_SIZEOF_VAR,
	TRANSLATE_COND_SIZEOF_INDEXVAR,
	TRANSLATE_COND_SIZEOF_ARRAY,
	TRANSLATE_COND_STRLEN_STR,
};
enum eTranslateOperand
{
	TRANSLATE_OPERAND_VAL,
	TRANSLATE_OPERAND_VAR,
	TRANSLATE_OPERAND_IDXVAR
};

struct sTypeNames
{
	char			m_szName[MAX_TYPE_NAME];
	PType			m_BasicType;
	const char	*	m_szID;
	bool			m_bIsPseudo;
	eTokenType		m_eTokenType;
};
struct sExtendedTypeNames
{
	char			m_szName[MAX_TYPE_NAME];
	TypeIndex		m_nTypeIdx;
	WORD			m_wType;
	const char *	m_szID;
	eTokenType		m_eTokenType;
};
struct sTypeCondition
{
	PType					m_bBasicType;
	eTranslateCondition		m_eConditions[MAX_TRANSLATION_CONDITIONS];
};

union uParam
{
	float f;
	int i;
	DWORD dw;
};

class CType;
class CExtendedType;
class CTypeBase;
class CToken;
class CLabel;
class COpcodes;
class COpcode;
class CCommand;
class CVariable;
class COperation;

class CSyntax
{
	static CList<CTypeBase>				s_TypeMap;
	static CLListEx<CTypeBase*>			s_TypeList;
	static const sTypeNames				s_pTypeNames[MAX_TYPES+1];
	static const sTypeCondition			s_TypeConditions[MAX_TYPES];
	static const sExtendedTypeNames		s_pExtendedTypeNames[NUM_BUILT_IN_EXTENDED_TYPES+1];
	static int							s_nNumExtendedTypes;
	static bool							s_bDefaultsLoaded;

	static CType					*	s_pTypes[MAX_TYPES];
	static CExtendedType			*	s_pExtendedTypes[MAX_BUILT_IN_EXTENDED_TYPES];

	static void AddType(TypeIndex t);
	static void AddExtendedType(ExtendedTypeIndex t);

public:
	static const struct sParseNames
	{
		char szName[32];
		int nFuncID;
	}						s_pParseOpcodeArray[SCRIPT_MAX_PARSED_OPCODES];
	static struct sDefinitions
	{
		bool bUsed;
		char szName[40];
		uParam nValue;
	}								s_pDefinitions[64];
	static const struct sDirectives
	{
		char szName[40];
		int nDirective;
	}						s_pDirectivesArray[NUM_DIRECTIVES];
	static struct sConstructs
	{
		char szName[40];
	}								s_pConstructs[NUM_LANG_CONSTRUCTS];

	static void LoadDefaultTypes();
	static void LoadTypes(const char *szFilePath);
	static void LoadScmInfo(const char *szFilePath);
	static void LoadScrInfo(const char *szFilePath);

	static void AddExtendedType(TypeIndex basic_type, const char * szId, const char * szName, eTokenType token_type);
	static void AddEnum(const char *szId, const class CEnum *pEnum);

	static int GetArrayIndexAndTokenize(char *szVarString, int &nArrayIdxOut)
	{
		char *szTmp;
		bool nResult = GetArrayIndexAndTokenize(szVarString, nArrayIdxOut, szTmp);

		// If we're using this function, we're probably looking for an array size specifier or strictly a const index
		if(szTmp) throw(SCRambl.GetText("ERRORS", "E1140"));
		return nResult;
	}
	static bool GetArrayIndexAndTokenize(char *szVarString, int &nArrayIdxOut, char *&szVarName);
	static const char * ReadIdentifier(const char * szStr, char * szOut)
	{
		int i=0;
		while(IsIdentifierChar(szStr[i])) ++i;
		if(!i) return nullptr;
		if(i >= MAX_IDENTIFIER_NAME-1) return nullptr;
		szncpy(szOut, szStr, i+1);
		return &szStr[i];
	}

	static int GetDirective(const char * szName)
	{
		for(int i=0; i<NUM_DIRECTIVES; i++)
		{
			if(!*s_pDirectivesArray[i].szName) break;
			if(!stricmp(s_pDirectivesArray[i].szName, szName))
				return CSyntax::s_pDirectivesArray[i].nDirective;
		}
		return -1;
	}

	// Hex prefixed?
	static bool IsValueHexRepresented(const char *szVal)
	{
		return szVal[0] == '0' && (szVal[1] == 'x' || szVal[1] == 'X');
	}

	// Ummm...
	static bool IsValueName(const char *szVal)
	{
	}

	// +1 for each 'xx'
	static int CountHexValueBytes(const char *szVal)
	{
		int i;
		for(i=0; IsHex(*szVal); i++);
		i -= (i % 2);
		return i > 4 ? 4 : i;
	}

	/*static const char * GetBasicTypeName(TypeIndex type)
	{
		return s_pTypeNames[type].m_szName;
	}
	static const char * GetBasicTypeName(PType type)
	{
		for(int i=0; i<MAX_TYPES; i++)
		{
			if(*s_pTypeNames[i].m_szName)
			{
				if(s_pTypeNames[i].m_sType == type)
					return s_pTypeNames[i].m_szName;
			}
			else break;
		}
		return nullptr;
	}
	static const char * GetExtendedTypeName(WORD type)
	{
		for(int i=0; i<MAX_TYPES; i++)
		{
			if(*s_pExtendedTypeNames[i].m_szName)
			{
				if(s_pExtendedTypeNames[i].m_wType == type)
					return s_pExtendedTypeNames[i].m_szName;
			}
			else break;
		}
		return GetBasicTypeName(GetBasicTypeForExtendedType(type));
	}*/

	// Get construct via name...
	static int GetConstruct(const char *str)
	{
		for(int i=0; i < 64; i++)
		{
			if(!strcmp(s_pConstructs[i].szName, str)) return i;
		}
		return -1;
	}

	// In case you forgot it...
	static const char * GetConstructName(int id)
	{
		return s_pConstructs[id].szName;
	}

	// Fastest access to built-in types
	static inline const class CType				*	GetType(TypeIndex idx)					{return s_pTypes[idx.m_nIdx];}
	static inline const class CExtendedType		*	GetExtendedType(ExtendedTypeIndex idx)	{return idx.m_wIdx && idx.m_wIdx < MAX_BUILT_IN_EXTENDED_TYPES ? s_pExtendedTypes[idx.m_wIdx-1] : nullptr;}

	// Searchable type names
	static const class CTypeBase		*	GetTypeByName(const char *szName);

	// Get a type using it's short identifier, for definitions mostly
	static const CTypeBase		*	GetTypeByID(const char *szVal);
	
	// Access to basic attributes of built-in types (mostly used for adding them at startup)
	static inline const char	*	GetTypeID(TypeIndex idx)						{return s_pTypeNames[idx.m_nIdx].m_szID;}
	static inline const char	*	GetTypeID(ExtendedTypeIndex idx)				{return s_pExtendedTypeNames[idx.m_wIdx-1].m_szID;}
	static inline const char	*	GetTypeName(TypeIndex idx)						{return s_pTypeNames[idx.m_nIdx].m_szName;}
	static inline const char	*	GetTypeName(ExtendedTypeIndex idx)				{return s_pExtendedTypeNames[idx.m_wIdx-1].m_szName;}
	static inline PType				GetBasicType(TypeIndex idx)						{return s_pTypeNames[idx.m_nIdx].m_BasicType;}
	static inline PType				GetBasicType(ExtendedTypeIndex idx)				{return s_pTypeNames[s_pExtendedTypeNames[idx.m_wIdx-1].m_nTypeIdx.m_nIdx].m_BasicType;}
	static inline eTokenType		GetTokenType(TypeIndex idx)						{return s_pTypeNames[idx.m_nIdx].m_eTokenType;}
	static inline eTokenType		GetTokenType(ExtendedTypeIndex idx)				{return s_pExtendedTypeNames[idx.m_wIdx-1].m_eTokenType;}
	static inline bool				IsTypePseudo(TypeIndex idx)						{return s_pTypeNames[idx.m_nIdx].m_bIsPseudo;}

	/*static const char * GetStandardTypeName(int idx)
	{
		 return idx < sizeof(s_pTypeNames) && *s_pTypeNames[idx].m_szName ? s_pTypeNames[idx].m_szName : nullptr;
	}*/

	/*static char GetTypeByName(const char *name)
	{
		if(!name) return PT_NONE;
		for(int i=0; i<MAX_TYPES; i++)
		{
			if(s_pTypeNames[i].m_szName)
			{
				if(!strncmp(s_pTypeNames[i].m_szName, name, MAX_TYPE_NAME))
					return s_pTypeNames[i].m_sType;
			}
			else break;
		}
		return PT_NONE;
	}*/

	static bool IsOperatorChar(int c)
	{
		switch(c)
		{
		case OPERATOR_ADD:
		case OPERATOR_ASSIGN:
		case OPERATOR_CONVERT:
		case OPERATOR_DIVIDE:
		//case OPERATOR_EQUAL:
		case OPERATOR_DELTA:
		case OPERATOR_GREATER:
		case OPERATOR_LESSER:
		case OPERATOR_MULTIPLY:
		case OPERATOR_SUBTRACT:
		
		// custom operators
		case '%':
		case '&':
		case '|':
		case '^':
		case '~':
			return true;
		default:;
		}
		return false;
	}

	/*static const CType * GetBasicTypeByIdentifier(char iParam)
	{
		switch(ToLower(iParam))
		{
		case 'b':
		case 'p':
		case 'm':
		case 'i': return s_pTypes[TYPE_INT];
		case 'f': return s_pTypes[TYPE_FLOAT];
		case 'c': return s_pTypes[TYPE_CONST];
		case 't': return s_pTypes[TYPE_TEXT_LABEL];
		case 's': return s_pTypes[TYPE_STRING]
		case 'v': return s_pTypes[TYPE_VAR];
		case 'l': return s_pTypes[TYPE_LVAR];
		default:;
		}
		return nullptr;
	}*/
	static char GetIdentifierForTypeFlag(eArgFlag flag)
	{
		switch(flag)
		{
		case ARGFLAG_PARSABLE: return '@';
		case ARGFLAG_SIZE_LIMITED: return '.';
		case ARGFLAG_NUM_EXTRA_PARAMS: return '#';
		case ARGFLAG_VAR_EXTRA_PARAMS: return '+';
		case ARGFLAG_UNKNOWN_PARAM: return '?';
		case ARGFLAG_OMISSION: return '=';
		case ARGFLAG_ANY: return '*';
		}
		return '\0';
	}
	static eArgFlag GetTypeFlagByIdentifier(char iParam)
	{
		switch(iParam)
		{
		case '@': return ARGFLAG_PARSABLE;
		case '.': return ARGFLAG_SIZE_LIMITED;
		case '#': return ARGFLAG_NUM_EXTRA_PARAMS;
		case '+': return ARGFLAG_VAR_EXTRA_PARAMS;
		case '?': return ARGFLAG_UNKNOWN_PARAM;
		case '=': return ARGFLAG_OMISSION;
		case '*': return ARGFLAG_ANY;
		}
		return ARGFLAG_NONE;
	}
	static int GetValueType(const char *szCode)		// old
	{
		bool bIsNegated = false,
			 bBeginsWithNum = false,
			 bBeginsWithDQuote = false,
			 bBeginsWithSQuote = false,
			 bContainsAlphas = false,
			 bContainsAllHex = true,
			 bContainsAllNum = true,
			 bBeginsWithNaught = false,
			 bContainsDecPoint = false,
			 bIsCastedInt = false,
			 bIsCastedFloat = false,
			 bHexPrefix = false,
			 bEndsWithDQuote = false,
			 bEndsWithSQuote = false;
		char c;

		if(c = *szCode)
		{
			if(c == '-')
			{
				bIsNegated = true;
				szCode++;
			}
			if(c == '0')
			{
				bBeginsWithNaught = true;
				bBeginsWithNum = true;
				szCode++;
				if(c == 'x' || c == 'X')
				{
					bHexPrefix = true;
					szCode++;
				}
			}
			if(c == '\'')
			{
				bBeginsWithSQuote = true;
				szCode++;
			}
			if(c == '"')
			{
				bBeginsWithDQuote = true;
				szCode++;
			}
			if(IsDecimal(c)) bBeginsWithNum = true;
		}

		while(c = *szCode++)
		{
			if(!szCode[1] && (c == 'f' || c == 'i'))
			{
				if(c == 'i') bIsCastedInt = true;
				if(c == 'f') bIsCastedFloat = true;
			}
			else
			{
				if(c == '.')
				{
					bContainsDecPoint = true;
					bContainsAllHex = false;
					c = *szCode++;
				}
				if(IsAlphabetic(c)) bContainsAlphas = true;
				if(!IsDecimal(c)) bContainsAllNum = false;
				if(!IsHex(c)) bContainsAllHex = false;
			}
		}

		if(bBeginsWithNum)
		{
			// This is not a valid identifier, must be a value
			if((bIsCastedInt || bIsCastedFloat) && (bContainsAllNum || bContainsAllHex))
			{
				if(bIsCastedInt) return PT_INT;
				else return PT_FLOAT;
			}
			else
			{
				if(bHexPrefix && bContainsAllHex) return PT_INT;
				if(bContainsAllNum)
				{
					if(bContainsDecPoint) return PT_FLOAT;
					return PT_INT;
				}
			}
		}
		return PT_NONE;
	}

	static int GetBasicTypeIndex(PType type)
	{
		// ... so you dont have to...
		switch(type)
		{
		case PT_INT:					return TYPE_INT;
		case PT_FLOAT:					return TYPE_FLOAT;
		case PT_TEXT_LABEL:				return TYPE_TEXT_LABEL;
		case PT_STRING:					return TYPE_STRING;
		case PT_CONST:					return TYPE_CONST;
		case PT_VAR:					return TYPE_VAR;
		case PT_VAR_INT:				return TYPE_VAR_INT;
		case PT_VAR_FLOAT:				return TYPE_VAR_FLOAT;
		case PT_VAR_TEXT_LABEL:			return TYPE_VAR_TEXT_LABEL;
		case PT_VAR_STRING:				return TYPE_VAR_STRING;
		case PT_LVAR:					return TYPE_LVAR;
		case PT_LVAR_INT:				return TYPE_LVAR_INT;
		case PT_LVAR_FLOAT:				return TYPE_LVAR_FLOAT;
		case PT_LVAR_TEXT_LABEL:		return TYPE_LVAR_TEXT_LABEL;
		case PT_LVAR_STRING:			return TYPE_LVAR_STRING;
		case PT_VAR_INT_ARRAY:			return TYPE_VAR_INT_ARRAY;
		case PT_VAR_FLOAT_ARRAY:		return TYPE_VAR_FLOAT_ARRAY;
		case PT_VAR_TEXT_LABEL_ARRAY:	return TYPE_VAR_TEXT_LABEL_ARRAY;
		case PT_VAR_STRING_ARRAY:		return TYPE_VAR_STRING_ARRAY;
		case PT_LVAR_INT_ARRAY:			return TYPE_LVAR_INT_ARRAY;
		case PT_LVAR_FLOAT_ARRAY:		return TYPE_LVAR_FLOAT_ARRAY;
		case PT_LVAR_TEXT_LABEL_ARRAY:	return TYPE_LVAR_TEXT_LABEL_ARRAY;
		case PT_LVAR_STRING_ARRAY:		return TYPE_LVAR_STRING_ARRAY;
		}
		return TYPE_NONE;
	}

	static inline bool EvalLogic(bool bCurrent, bool bNotOp, bool bLogOr, bool bNew)
	{
		// oh the binary goodness
		return bNew ? (bLogOr ? bCurrent | !bNotOp : bCurrent & !bNotOp) : (bLogOr ? bCurrent | bNotOp : bCurrent & bNotOp);
	}
	static inline int EvaluateExpression(int lVal, eExprOp eOperation, int rVal = 0, bool bLogicalNot = false, bool bLogicalOp = false)
	{
		switch(eOperation)
		{
		// Unary
		case EXPR_OP_INC:		return ++lVal;
		case EXPR_OP_DEC:		return --lVal;
		case EXPR_OP_POS:		return +lVal;
		case EXPR_OP_NEG:		return -lVal;
		case EXPR_OP_BIT_NOT:	return ~lVal;
		
		// Arithmetic
		case EXPR_OP_ASSIGN:		return rVal;
		case EXPR_OP_ADD:		return lVal + rVal;
		case EXPR_OP_SUBTRACT:	return lVal - rVal;
		case EXPR_OP_MULTIPLY:	return lVal * rVal;
		case EXPR_OP_DIVIDE:		return lVal / rVal;

		// Bitwise
		case EXPR_OP_BIT_AND:	return lVal & rVal;
		case EXPR_OP_BIT_OR:		return lVal | rVal;
		case EXPR_OP_BIT_XOR:	return lVal ^ rVal;
		case EXPR_OP_BIT_LSH:	return lVal << rVal;
		case EXPR_OP_BIT_RSH:	return lVal >> rVal;
		case EXPR_OP_BIT_MOD:	return lVal % rVal;

		// Comparison
		case EXPR_OP_EQUAL:		return EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, lVal == rVal);
		case EXPR_OP_INEQUAL:	return EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, lVal != rVal);
		case EXPR_OP_EQGREATER:	return EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, lVal >= rVal);
		case EXPR_OP_EQLESSER:	return EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, lVal <= rVal);
		case EXPR_OP_GREATER:	return EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, lVal > rVal);
		case EXPR_OP_LESSER:		return EvalLogic(lVal ? true : false, bLogicalNot, bLogicalOp, lVal < rVal);
		default:;
		}
		throw(SCRambl.GetText("ERRORS", "E1133"));
	}
};

class CControlStruct
{
	friend class CControlStructs;

	CControlStruct	*	m_pParent;
	CControlStruct	*	m_pNext;
	CControlStruct	*	m_pPrev;
	CToken			*	m_pTokens[4];			// tokens used for 'repeat', 'for', etc. that are needed at the loop end
	CLabel			*	m_pLabelBegin;
	CLabel			*	m_pLabelEnd;
	int					m_nStartLine;
	int					m_nDepth;
	int					m_nConstruct;
	bool				m_bIsLoop;

	CControlStruct(int nDepth, int nConstruct)
	{
		m_pParent = nullptr;
		m_pNext = nullptr;
		m_pPrev = nullptr;
		m_pLabelBegin = nullptr;
		m_pLabelEnd = nullptr;
		m_nStartLine = 0;
		m_nDepth = nDepth;
		m_nConstruct = nConstruct;
		memset(m_pTokens, 0, sizeof(m_pTokens));
	}
	~CControlStruct()
	{
		if(m_pNext) delete m_pNext;
	}

public:
	inline void IsLoop(bool b)								{m_bIsLoop = b;}
	inline bool IsLoop()									{return m_bIsLoop;}
	inline void SetToken(CToken *pToken, int i=0)			{m_pTokens[i] = pToken;}
	inline void SetConstruct(int nVal)						{m_nConstruct = nVal;}
	inline void SetBeginLabel(CLabel *pLabel)				{m_pLabelBegin = pLabel;}
	inline void SetEndLabel(CLabel *pLabel)					{m_pLabelEnd = pLabel;}
	inline void SetLineDeclared(int nLine)					{m_nStartLine = nLine;}
	inline CControlStruct * GetParent()						{return m_pParent;}
	inline CToken * GetToken(int i)							{return m_pTokens[i];}
	inline int GetConstruct()								{return m_nConstruct;}
	inline CLabel * GetBeginLabel()							{return m_pLabelBegin;}
	inline CLabel * GetEndLabel()							{return m_pLabelEnd;}
	inline int GetLineDeclared()							{return m_nStartLine;}
};
class CControlStructs
{
	static CControlStruct	*	s_pFirst;
	static CControlStruct	*	s_pLast;
	static int					s_nNumControlStructs;

public:
	static CControlStruct * Add(int nConstruct, int nDepth = 0, CControlStruct *pParent = nullptr)
	{
		CControlStruct *pStruct = new CControlStruct(nDepth, nConstruct);

		if(s_nNumControlStructs)
		{
			pStruct->m_pParent = pParent;
			pStruct->m_pPrev = s_pLast;
			s_pLast->m_pNext = pStruct;
		}
		else s_pFirst = pStruct;

		s_pLast = pStruct;
		s_nNumControlStructs++;
		return pStruct;
	}

	static void Reset()
	{
		if(s_pFirst)
		{
			delete s_pFirst;
			s_pFirst = nullptr;
		}
	}
};

extern const int		&MAX_VAR_INDEX;
extern const DWORD		&MAIN_VAR_BLOCK_OFFSET;
extern const DWORD		&NUM_BYTES_IN_ARRAY_FLAG;
extern const int		&MAX_CONDITIONS;
extern const int		&LOGICAL_AND;
extern const int		&LOGICAL_OR;
extern const int		&LOGICAL_NOT;
extern const int		&PREFIX_LOGICAL_AND;
extern const int		&PREFIX_LOGICAL_OR;
extern const int		&PREFIX_LOGICAL_NOT;